﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace GingerBeard.Utilities
{
    public class CmdLineArgs
    {
		public class MissingRequiredArgException : Exception
		{
			public MissingRequiredArgException(String message) : base(message)
			{

			}
		}

		public interface ISpecialArgHandler
		{
			void Handle(SupportedArg arg, String value);
		}

		public class InputFileHandler : ISpecialArgHandler
		{
			public void Handle(SupportedArg arg, String value)
			{
				if (!File.Exists(value))
				{
					throw new FileNotFoundException("Input file doesn't exist", value);
				}
			}
		}

		public class EnumHandler : ISpecialArgHandler
		{
			private String[] values;
			public EnumHandler(String[] inValues)
			{
				values = inValues;
			}
			public void Handle(SupportedArg arg, String value)
			{
				foreach (String v in values)
				{
					if (v.ToLower() == value.ToLower())
						return;
				}
				throw new Exception(String.Format("Invalid option '{0}' specified for argument '{1}'", value, arg.Name));
			}
		}

        public class SupportedArg
        {
            public enum RequirementType
            {
                Required,
                Optional,
            };

			public SupportedArg(String inName, String inDesc, RequirementType inReqType)
			{
				name = inName;
				desciption = inDesc;
				requirement = inReqType;
				handler = null;
			}

            public SupportedArg(String inName, String inDesc, RequirementType inReqType, ISpecialArgHandler inHandler)
            {
                name = inName;
                desciption = inDesc;
                requirement = inReqType;
				handler = inHandler;
            }

            private String name;
            private String desciption;
            private RequirementType requirement;
			private ISpecialArgHandler handler;

            public String Name
            {
                get { return name; }
                set { name = value; }
            }

            public String Description
            {
                get { return desciption; }
                set { desciption = value; }
            }

            public RequirementType Requirement
            {
                get { return requirement; }
                set { requirement = value; }
            }

			public ISpecialArgHandler Handler
			{
				get { return handler; }
				set { handler = value; }
			}
        }

        private Dictionary<String, String> args = new Dictionary<String, String>();
        private List<SupportedArg> supportedArgs = new List<SupportedArg>();

        public List<SupportedArg> SupportedArgs
        {
            get { return supportedArgs; }
        }

        public void Initialise(String[] inArgs)
        {
			List<String[]> stuffToProcess = new List<String[]>();
			stuffToProcess.Add(inArgs);
			while (stuffToProcess.Count > 0)
			{
				String[] cur = stuffToProcess[0];
				stuffToProcess.RemoveAt(0);
				foreach (String inArg in cur)
				{
					if (inArg.StartsWith("/"))
					{
						String theArg = inArg.Substring(1);
						int colonPos = theArg.IndexOf(':');
						if (colonPos != -1)
						{
							String theSwitch = theArg.Substring(0, colonPos);
							String theValue = theArg.Substring(theArg.IndexOf(':') + 1);
							if (theValue.StartsWith("@"))
							{
								theValue = theValue.Substring(1);
								if (File.Exists(theValue))
								{
									theValue = File.ReadAllText(theValue);
								}
								else
								{
									throw new Exception("Response file '" + theValue + "' doesn't exist");
								}
							}
							args[theSwitch] = theValue;
						}
						else
						{
							args[theArg] = "";
						}
					}
					else if (inArg.StartsWith("@"))
					{
						String filename = inArg.Substring(1);
						if (File.Exists(filename))
						{
							String newStuff = File.ReadAllText(filename);
							String[] bits = newStuff.Split(' ');
							List<String> mergedBits = new List<String>();
							String workingString = null;
							foreach (String bit in bits)
							{
								if (bit.Contains("\""))
								{
									if (workingString == null)
										workingString = bit;
									else
									{
										workingString += " " + bit;
										mergedBits.Add(workingString);
										workingString = null;
									}
								}
								else
								{
									mergedBits.Add(bit);
								}
							}
							
							stuffToProcess.Add(mergedBits.ToArray());
						}
						else
						{
							throw new Exception("Response file '" + filename + "' doesn't exist");
						}
					}
					else
					{
						args["IN"] = inArg;
					}
				}
            }

            // validate args
            foreach (SupportedArg supportedArg in supportedArgs)
            {
                if (supportedArg.Requirement == SupportedArg.RequirementType.Required)
                {
                    if (!args.ContainsKey(supportedArg.Name))
                    {
						throw new Exception("Required argument '" + supportedArg.Name + "' is missing!");
                    }
                }

				if (supportedArg.Handler != null)
				{
					if (args.ContainsKey(supportedArg.Name))
					{
						supportedArg.Handler.Handle(supportedArg, args[supportedArg.Name]);
					}
				}
            }
        }

        public String this[String key]
        {
            get 
			{
				if (args.ContainsKey(key))
					return args[key];
				else 
					return null;
			}
            set { args[key] = value; }
        }

        public void PrintUsage()
        {
            int longestName = 0;
            foreach (SupportedArg arg in supportedArgs)
            {
                longestName = Math.Max(longestName, arg.Name.Length);
            }
            int nameLengh = longestName + 1;

            foreach (SupportedArg arg in supportedArgs)
            {
                String line = arg.Name;
                for (int i = line.Length; i < nameLengh; ++i)
                    line += " ";
                System.Console.WriteLine(String.Format("/{0}- {1}", line, arg.Description));
            }
        }
    }
}
